/**
 * 
 */
package strain.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;

import strain.domain.InconsistentDomainException;

public class DiscreteVariable<T>{
    
    private static int reductionCount = 0;
    private static int nonReductionCount = 0;
    private static int reducedToEmptyCount = 0;
    
    public static int getReductionCount(){
        return reductionCount;
    }
    
    public static int getNonReductionCount(){
        return nonReductionCount;
    }
    
    public static int getReducedToEmptyCount(){
        return reducedToEmptyCount;
    }
    
    private final String name;
    private final Set<T> domain;
    
    public DiscreteVariable(final String name, final Set<T> domain){
        if (name == null) throw new IllegalArgumentException("variable must have a non-null name");
        if (domain == null) throw new IllegalArgumentException("variable must have a non-null domain");
        this.name = name;
        this.domain = new TreeSet<T>(domain);
    }
    
    public DiscreteVariable<T> copy(){
        return new DiscreteVariable<T>(this.name, this.domain);
    }
    
    public String getName(){
        return this.name;
    }
    
    public Set<T> getDomain(){
        return Collections.unmodifiableSet(this.domain);
    }
    
    public int getSize(){
        return this.domain.size();
    }
    
    public boolean isBound(){
        return getSize() == 1;
    }
    
    public T getBoundValue(){
        if (!isBound())
            throw new IllegalStateException("can't call .getBoundValue() on a variable that is not .isBound()");
        
        return this.domain.iterator().next();
    }
    
    public boolean isEmpty(){
        return getSize() == 0;
    }
    
    public boolean remove(final T value) throws InconsistentDomainException{
        Set<T> values = new TreeSet<T>();
        values.add(value);
        return remove(values);
    }
    
    public boolean remove(final Collection<T> values) throws InconsistentDomainException{
        boolean result = this.domain.removeAll(values);
        if (this.domain.isEmpty()){
            ++reducedToEmptyCount;
            throw new InconsistentDomainException(getName() + " reduced to empty");
        }
        if (result) ++reductionCount;
        else ++nonReductionCount;
        return result;
    }
    
    public boolean bind(final T value) throws InconsistentDomainException{
        Collection<T> values = new ArrayList<T>(1);
        values.add(value);
        return retain(values);
    }
    
    public boolean retain(final Collection<T> values) throws InconsistentDomainException{
        boolean result = this.domain.retainAll(values);
        if (this.domain.isEmpty()){
            ++reducedToEmptyCount;
            throw new InconsistentDomainException(getName() + " reduced to empty");
        }
        if (result) ++reductionCount;
        else ++nonReductionCount;
        return result;
    }
    
    @Override
    public String toString(){
        return "[" + this.name + ":" + this.domain + "]";
    }
    
    @Override
    public int hashCode(){
        return this.name.hashCode();
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(final Object o){
        if (this == o) return true;
        if ((o == null) || (!this.getClass().equals(o.getClass()))) return false;
        DiscreteVariable<T> dv = (DiscreteVariable<T>) o;
        return this.name.equals(dv.name) && this.domain.equals(this.domain);
    }
    
}